package hotciv.variants;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Map;

import hotciv.common.*;
import hotciv.framework.*;
import hotciv.strategies.AttackStrategy;
import hotciv.strategies.MoveUnitStrategy;

public class AlphaCivMoveUnitStrategy implements MoveUnitStrategy {

	
	/**
	 * Will calculate the distance between two tiles, so distance 0 returns 0, distance 1 returns 1 and distance >1 returns an integer >1 (not nessesarily the right distance).
	 * Precondition: from and to are not null
	 * @param from position
	 * @param to position
	 * @return the distance between the two tiles so dist 0 ret 0, dist 1 ret 1 but dist >1 ret and int >1 (not nessesarily the right distance).
	 */
	public int distanceBetweenTiles(ModifiableGame game, Position from, Position to) {
		int dx = Math.abs(to.getRow()-from.getRow());
		int dy = Math.abs(to.getColumn()-from.getColumn());
		int dist = (int) Math.floor(Math.sqrt(dx*dx + dy*dy));
		return dist;
	}
	
	/**
	 * Returns whether a tile is walkable by a unit 
	 * @param p - the position to check
	 * @return whether the tile at position p is walkable
	 */
	public boolean isWalkableTile(ModifiableGame game, Position p) {
		Tile tile = game.getTileAt(p);
		
		if (tile.getTypeString() == GameConstants.OCEANS) return false;
		if (tile.getTypeString() == GameConstants.MOUNTAINS) return false;
		
		return true;
	}
	
	public boolean unitOccupiedTile(ModifiableGame game, Position p) {
		Unit u = game.getUnitAt(p);
		if (u == null) return false;
		
		return true;
	}
	
	@Override
	public boolean moveUnit(ModifiableGame game, Position from, Position to, AttackStrategy ccs) {
		/* If tile isn't walkable, return false */
		if (isWalkableTile(game, to) == false) return false;
		
		/* If the tile is occupied, check if the unit is of same color, if so, return false */
		if (unitOccupiedTile(game, to)) {
			Player fromUnitOwner = game.getUnitAt(from).getOwner();
			Player toUnitOwner = game.getUnitAt(to).getOwner();
			if (fromUnitOwner == toUnitOwner) return false;
		}
		
		/* If distance is larger than move count for the unit, return false */
		Map<Position, UnitImpl> units = game.getUnits();
		UnitImpl u = units.get(from);
		int distance = distanceBetweenTiles(game, from, to);
		if (u.getMoveCount() < distance) return false;
		
		/* If the unit is not owned by player, he can not move it */
		if(game.getPlayerInTurn() != u.getOwner()) return false;
		
		/* From now on, we know the move is valid */
		
		/* If the other tile is occupied by another unit, it is a hostile one.
		 * Calculate the outcome */
		if (game.getUnitAt(to) != null) {
			// There is a unit at the to-position
			boolean theAttackerIsTheWinner = ccs.calculateOutcome(game, from, to);
			if (theAttackerIsTheWinner == false) {
				// The attacker lost the battle
				units.remove(from); // Remove the attacker
				return true; // Stop the method here, telling the move is valid (even though the attacker lost)
			}
			// Else continue performing the move

			/* Insert the winner of this battle in the attacks-map */
			Map<Integer, Collection<Player>> attacks = game.getWonAttacks();
			int thisAge = game.getAge();
			Collection<Player> thisYearsAttacks = attacks.get(thisAge);
			if (thisYearsAttacks == null) {
				thisYearsAttacks = new ArrayList<Player>();
			}
			thisYearsAttacks.add(game.getUnitAt(from).getOwner());
			attacks.put(thisAge, thisYearsAttacks);
			
		}
		
		/* Move the unit and eventually replace anther unit at the to position */
		units.remove(from);
		units.put(to, u);
		u.setMoveCount(0);
		
		/* Eventually conquour city */
		City movedToCity = game.getCityAt(to);
		if (movedToCity != null && movedToCity.getOwner() != u.getOwner()) {
			/* Conquour city */
			((CityImpl) movedToCity).setOwner(u.getOwner());
		}
		
		return true;
	}

}
